#include "devil1geo.h"
#include <stdio.h>

static void printgheader(struct Header*);

static void printmeshheader(struct MeshHeader*);

static void printmeshbatch(struct Batch*);

static void printcoordinate(struct Coordinate*, unsigned int);

static bool getgheader(struct Header**, const char*);

static bool getmeshheader(struct MeshHeader**, unsigned int i, const char * const);

static bool getmeshbatch(struct Batch*, unsigned int offset, const char * const);

static bool getmesh(struct Mesh*, unsigned int i, const char*, unsigned int filesize);

fn_devil1geo const DEVIL1GEO = {printgheader,
                                printmeshheader,
                                printmeshbatch,
                                printcoordinate,
                                getgheader,
                                getmeshheader,
                                getmeshbatch,
                                getmesh};

static void printgheader(struct Header *gh) {
    if (gh != NULL) {
        printf("pointer %p\n", gh);
        printf("number of meshes %x\n", gh -> numMesh);
        printf("unknown number B %x\n", gh -> unknownNumberB);
        printf("unknown number C %x\n", gh -> unknownNumberC);
        printf("unknown number D %x\n", gh -> unknownNumberD);
        printf("padding %x\n",          gh -> padding);
        printf("unknown offset %lx\n",  gh -> unknownOffset);
    }
}

static void printmeshheader(struct MeshHeader *mh) {
    if (mh == NULL) {
        return;
    }
    printf("number of batches %x\n",  mh -> numBatch);
    printf("number of vertices %x\n", mh -> numVertex);
    printf("unknown %x\n",            mh -> u);
    printf("batch offset %lx\n",      mh -> offsetBatches);
    printf("flags %lx\n\n",           mh -> flags);
}

static void printmeshbatch(struct Batch *b) {
    if (b == NULL) {
        return;
    }
    struct BatchData *bd = b -> bd;
    printf("number of vertices %x\n", bd -> numVertex);
    printf("unknown byte %x\n",       bd -> uB);
    printf("padding %x\n",            bd -> padding);
    printf("offsetPositions %lx\n",   bd -> offsetPositions);
    printf("offsetNormals %lx\n",     bd -> offsetNormals);
    printf("offsetUVs %lx\n",         bd -> offsetUVs);
    printf("offsetBoneIndexes %lx\n", bd -> offsetBoneIndexes);
    printf("offsetBoneWeights %lx\n", bd -> offsetBoneWeights);
    printf("offsets %lx\n\n",         bd -> offsets[0]);
    printcoordinate(b -> vd.positions, 3);
}

static void printcoordinate(struct Coordinate *p, unsigned int count) {
    if (p == NULL) {
        return;
    }
    unsigned int i;
    for (i = 0; i < count; i++) {
        printf("(%f, %f, %f)\n", (p[i]).x, (p[i]).y, (p[i]).z);
    }
}

static bool getgheader(struct Header** h, const char* filedata) {
    if (filedata == NULL) {
        return false;
    }
    (*h) = (struct Header*)filedata;
    return true;
}

static bool getmeshheader(struct MeshHeader **hs,
                   unsigned int i,
                   const char * const filedata) {
    bool done        = false;
    struct Header *h = NULL;
    if (hs == NULL || !(getgheader(&h, filedata))) {
        return done;
    }
    if (h -> numMesh < i) {
        return done;
    }
    unsigned int pos = sizeof(struct MeshHeader) * i + sizeof(struct Header);
    (*hs)   = (struct MeshHeader*)(filedata + pos);
    done = true;
    return done;
}

static bool getmeshbatch(struct Batch *b, 
                  unsigned int offset, 
                  const char * const filedata) {
    bool done = false;
    if (b == NULL || filedata == NULL) {
        return done;
    }
    struct BatchData  *d1 = NULL;
    struct VertexData d2;
    d1            = (struct BatchData*)  (filedata + offset);
    d2.positions  = (struct Coordinate*) (filedata + (d1 -> offsetPositions));
    d2.normals    = (struct Coordinate*) (filedata + (d1 -> offsetNormals));
    d2.u          = (struct UVs*)        (filedata + (d1 -> offsetUVs));
    d2.bi         = (struct BoneIndexes*)(filedata + (d1 -> offsetBoneIndexes));
    d2.bw         = (struct BoneWeights*)(filedata + (d1 -> offsetBoneWeights));
    b -> bd = d1;
    b -> vd = d2;
    done = true;
    return done;
}

// assume client has allocated memory for mesh
static bool getmesh(struct Mesh *m,
             unsigned int i,
             const char * const filedata,
             unsigned int filesize) {
    bool done = false;
    if (m == NULL || filedata == NULL || m -> b == NULL) {
        return done;
    }
    struct MeshHeader *mh = NULL;
    bool ok = getmeshheader(&mh, i, filedata);
    if (ok) {
        unsigned int j;
        struct Batch b;
        for (j = 0; j < mh -> numBatch; j++) {
            unsigned int offset = mh->offsetBatches + j * sizeof(struct BatchData);
            if (offset > filesize) {
              return done;
            }
            getmeshbatch(&b, offset, filedata);
//            printmeshbatch(&b);
            m -> b[j] = b;
        }
        done = true;
    }
    return done;
}


